﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;


namespace TunaInterpreter2.Classes
{

    public class ScriptPreprocesor
    {
        private string source;
        private MainWindow mv;
        private bool errors;
        private ScriptData sd;

        public ScriptPreprocesor(string source, MainWindow mv, ScriptData sd)
        {
            this.source = source;
            this.mv = mv;
            this.sd = sd;
        }

        private void logError(string error, int numLine)
        {
            this.mv.Print(ConsoleMsgType.ERROR, error + " at " + numLine + " line.");
            this.errors = true;
        }

        public bool parse()
        {
            this.errors = false;

            string[] reservedKeywords = { "int", "float", "string", "jumpnij", "print", "println" };

            string[] lines = source.Split(new[] { '\r', '\n' });

            this.sd.source = new string[lines.Length];

            string labelPattern = @"^(:)\w+\s*$";
            Regex label = new Regex(labelPattern);
            string intDefPattern = @"^int\s*([a-zA-Z]\w*)\s*$";
            Regex intDef = new Regex(intDefPattern);
            string floatDefPattern = @"^float\s*([a-zA-Z]\w*)\s*$";
            Regex floatDef = new Regex(floatDefPattern);
            string stringDefPattern = @"^string\s*([a-zA-Z]\w*)\s*$";
            Regex stringDef = new Regex(stringDefPattern);
            string jumpnijProcPattern = @"^jumpnij\s*\w*\s*$";
            Regex jumpnijProc = new Regex(jumpnijProcPattern);
            string printProcPattern = @"^print\s*(([a-zA-Z]\w*)|(\%[^%]*\%))\s*$";
            Regex printProc = new Regex(printProcPattern);
            string printlnProcPattern = @"^println\s*(([a-zA-Z]\w*)|(\%[^%]*\%))\s*$";
            Regex printlnProc = new Regex(printlnProcPattern);
            string ifOpPattern = @"^(([a-zA-Z]\w*)|(\d+(\.\d+)?))\s*?((\=\=)|(\!\=)|(\<)|(\>))\s*?(([a-zA-Z]\w*)|(\d+(\.\d+)?))\s*?\?\s*\w+\s*\:\s*\w+\s*$";
            Regex ifOp = new Regex(ifOpPattern);
            string assignOpPattern = @"^(\w+\s*?=(((\s*(([a-zA-Z]\w*)|(\d+(\.\d+)?))\s*)([\+\-\*\/]\s*(([a-zA-Z]\w*)||(\d+(\.\d+)?))\s*)?)|(\s*?%[^%]*%\s*)))$";
            Regex assignOp = new Regex(assignOpPattern);
            int i = 0;
            foreach (string line in lines)
            {
               
                string currentLine = line.Trim();
                if (currentLine.Trim().Length > 0)
                {
                    i++;
                    if (currentLine.ToCharArray()[0] == ':')
                    {
                        if (label.IsMatch(currentLine))
                        {
                            currentLine = trimSpace(currentLine);
                            if (!sd.addLabel(currentLine, i))
                            {
                                logError("Label already defined", i);
                            }
                            else
                            {
                                sd.source[i] = currentLine;
                            }
                            
                            continue;
                        }
                        logError("Syntax error in label definition", i);
                    }

                    if (currentLine.Contains('?') && currentLine.Contains(':') && !currentLine.Contains('\'') && !currentLine.Contains('\"'))
                    {
                        if (ifOp.IsMatch(currentLine))
                        {
                            sd.source[i] = currentLine;
                            continue;
                        }
                        logError("Syntax error in IF", i);
                    }

                    string[] lineElements = currentLine.Split(new[] { ' ', '=' });
                    if (lineElements.Count() > 1 && reservedKeywords.Contains(lineElements[0]))
                    {
                        switch (lineElements[0])
                        {
                            case "int":
                                if (intDef.IsMatch(currentLine))
                                {
                                    if (!sd.addInt(currentLine)) {
                                        logError("Variable already defined", i);
                                    };
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                logError("Syntax error int definition", i);
                                break;
                            case "float":
                                if (floatDef.IsMatch(currentLine))
                                {
                                    if (!sd.addFloat(currentLine))
                                    {
                                        logError("Variable already defined", i);
                                    };
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                logError("Syntax error float definition", i);
                                break;
                            case "string":
                                if (stringDef.IsMatch(currentLine))
                                {
                                    if (!sd.addString(currentLine))
                                    {
                                        logError("Variable already defined", i);
                                    };
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                logError("Syntax error string definition", i);
                                break;
                            case "jumpnij":
                                if (jumpnijProc.IsMatch(currentLine))
                                {
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                sd.source[i] = currentLine;
                                logError("Syntax error in jump call", i);
                                break;
                            case "print":
                                if (printProc.IsMatch(currentLine))
                                {
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                logError("Syntax error in print call", i);
                                break;
                            case "println":
                                if (printProc.IsMatch(currentLine))
                                {
                                    sd.source[i] = currentLine;
                                    continue;
                                }
                                logError("Syntax error in println call", i);
                                break;

                        }
                    }
                    if (assignOp.IsMatch(currentLine))
                    {
                        sd.source[i] = currentLine;
                        continue;
                    }
                    logError("Syntax error", i);
                }
                
            }
            return !this.errors;
        }

        private string trimSpace(string line)
        {
            return trimSpace(line, 0, line.Length);
        }

        private string trimSpace(string line, int from, int to)
        {
            string resultString;
            if (from == 0 && to == line.Length - 1)
            {
                resultString = line;
            }
            else
            {
                resultString = line.Substring(from, to);
                resultString = Regex.Replace(resultString, @"\s*", " ");
            }
            return resultString;
        }

        private string trimSpaceStringLine(string line)
        {
            string resultLine = "";
            int beforeString = line.IndexOf('%');
            int afterString = line.LastIndexOf("%");
            resultLine += trimSpace(line, 0, beforeString);
            resultLine += line.Substring(beforeString, afterString + 1);
            return resultLine;
        }

        public ScriptData GetData()
        {
            return this.sd;
        }

    }
}
